#include "GameLoopServer.h"
#include <iostream>
#include "bitboard.h"

// ----------
// inicializace objektu GameLoopServer
GameLoopServer::GameLoopServer(ComServer *aServer, std::string logFilename) {
	this->server = aServer;

	this->players[0] = "";
	this->players[1] = "";

	this->currentBoard.NewGame();
	this->log = new GameLog();
	this->log->Create(logFilename);

	this->lastMove.From = -1;
}

// -----------
// spusti smycku zajistujici beh hry na serveru
void GameLoopServer::Run() {
	// navaze spojeni s klienty
	if(this->ShakeHand(WHITE) == false || this->ShakeHand(BLACK) == false) {
		return;
	}
	
	this->log->LogPlayers(this->players);

	// hrat zacina cerny hrac
	int currentPlayer = WHITE;
	int winner = -1;

	this->log->StartMovesSection();

	// hrajeme dokud neni hra ve stavu odpovidajimu konci hry
	while(this->currentBoard.IsEndGame(&winner, (CheckerColor)currentPlayer) == false) {
		cout << "Hraje hrac: " << currentPlayer << endl;

		// vyzada si od klienta jeho tah a zpracuje ho
		this->ProcessMove(currentPlayer);

		this->currentBoard.Print();
		// zmeni hrace na protivnika
		(currentPlayer == BLACK)? currentPlayer = WHITE : currentPlayer = BLACK;

		cout << endl << endl;
	}

	this->log->EndMovesSection();

	// zasle zpravy o konci hry klientum
	this->EndGame(winner);
	this->log->LogWinner(winner);
	this->log->Close();
}

// -----------
// Vyzada si od klienta jeho tah a zpracuje ho
// int clientNumber				cislo klienta, ktery ma hrat
bool GameLoopServer::ProcessMove(int clientNumber) {
	// -----------
	// vyzada si od klienta tah
	ComData requestMove;
	requestMove.Msg = DO_MOVE;
	requestMove.CurrentBoard = this->currentBoard;

	// posilame posledni tah soupere - kvuli vizualizaci
	requestMove.CurrentMove = this->lastMove;
	server->SendData(requestMove, clientNumber);

	// -----------
	// prijme od klienta tah
	ComData requestMoveResp;
	server->RecieveData(&requestMoveResp, clientNumber);
	requestMoveResp.CurrentMove.Player = (CheckerColor)clientNumber;

	if(requestMoveResp.Msg != MOVE_SENT) {
		this->Error("Wrong message (expecting MOVE_SENT)");
		return false;
	}

	// ----------
	// zkontroluje tah zaslany klientem
	if(this->currentBoard.IsValidMove(requestMoveResp.CurrentMove)) {
		ComData moveConfirmation;

		// pokud je tah platny tak ho provedeme
		if(this->currentBoard.DoMoveAndCheck(requestMoveResp.CurrentMove)) {
			moveConfirmation.Msg = MOVE_OK;
			cout << "Tah OK" << endl;
		}
		else {
			moveConfirmation.Msg = MOVE_DID_NOT_CAPTURE;
			cout << "Tah MOVE_DID_NOT_CAPTURE" << endl;
		}

		moveConfirmation.CurrentBoard = this->currentBoard;
		server->SendData(moveConfirmation, clientNumber);

		this->log->LogMove(requestMoveResp.CurrentMove);

		this->lastMove = requestMoveResp.CurrentMove;
		return true;
	}
	// klient odeslal neplatny tah
	else {
		ComData moveInvalid;
		moveInvalid.Msg = MOVE_INVALID;
		server->SendData(moveInvalid, clientNumber);

		cout << "Tah MOVE_INVALID" << endl;

		// ukonceni hry
		this->Error("Invalid move.");
		return false;
	}

	return false;
}
// -----------
// Uvodni vymena inofrmaci na zacatku partie - identifikace kliena, urceni pravidel, barvy
bool GameLoopServer::ShakeHand(int clientNumber) {
	// ----------
	// Recv HELLO - ceka na inicializaci spojeni od klienta - zprava 'HELLO'
	ComData helloMsg;
	server->RecieveData(&helloMsg, clientNumber);
	if(helloMsg.Msg == HELLO && helloMsg.Player == clientNumber) {
		
		this->players[clientNumber] = helloMsg.Identification;

		ComData okMsg;
		okMsg.Msg = OK;
		server->SendData(okMsg, clientNumber);
	}
	else {
		// pokud klient posle neco jineho nez HELLO message jako prvni, nekomunikuje podle protokolu => zrusit spojeni
		this->Error("Connection refused.");
		return false;
	}
	
	return true;
}

// ----------
// Ukonci spojeni obou klientu (
void GameLoopServer::Error(std::string errorDesc) {
	// -----------
	// posle ukoncovaci zpravu oboum klientum
	ComData errorMsg;
	errorMsg.Msg = GAME_ERROR;
	errorMsg.errorMsg = errorDesc;

	server->SendData(errorMsg, WHITE);
	server->SendData(errorMsg, BLACK);
}

// ----------
// zasle zpravy o konci hry klientum
// int winner			cislo klienta, ktery vyhral; -1 pokud hra konci remizou
void GameLoopServer::EndGame(int winner) {
	if(winner != -1) {
		cout << "Vitez: " << winner << endl;

		// zprava vitezi
		ComData winnerMsg;
		winnerMsg.CurrentBoard = this->currentBoard;
		winnerMsg.Msg = GAME_WON;
		server->SendData(winnerMsg, winner);
		
		// zprava porazenemu
		int looser;
		(winner == WHITE) ? looser = BLACK : looser = WHITE;

		ComData looserMsg;
		looserMsg.CurrentBoard = this->currentBoard;
		looserMsg.Msg = GAME_LOST;
		server->SendData(looserMsg, looser);
	}
	// zprava o remize
	else {
		cout << "Remiza" << endl;

		ComData gameDrawnMsq;
		gameDrawnMsq.CurrentBoard = this->currentBoard;
		gameDrawnMsq.Msg = GAME_DRAWN;

		server->SendData(gameDrawnMsq, WHITE);
		server->SendData(gameDrawnMsq, BLACK);
	}
}